cli: adapter schema --type / sensors --key + api-key create --store-secret#301
Merged
Merged
Conversation
…tore-secret Three FDE-acceleration additions found while profiling AI agent build runs: - cloud-adapter/external-adapter `schema --type <t>`: resolve one adapter type's config sub-schema from the hive JSON-Schema $defs and render the flat field table (reusing hive's _flatten_schema). Shows where each field lives (e.g. hostname under client_options), preventing "unknown field" rejections from hand-built records. Unknown type errors with the valid type list. - cloud-adapter/external-adapter `sensors --key <adapter>`: return the live sensor(s) an adapter produced by matching the record's installation_key (iid) against the sensor iid — the reliable way to get an adapter's SID without decoding the installation key. Falls back to hostname match; reports a clear "not registered yet" when the adapter hasn't delivered events. - `api-key create --store-secret <name>` (+ --store-secret-tag): atomically mint the key and write its value into the secret hive, collapsing the mint -> capture -> store -> reference chain into one call (the value never has to transit an intermediate file). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r selector, etag-safe store-secret - adapter `schema --type` now respects --quiet (consistent with sensors/list-types) and its --output json carries the root $defs so nested $refs stay resolvable. - adapter `sensors --key` filters server-side via a sensor selector (iid == "…" / hostname == "…") instead of paging and scanning every sensor. - api-key create --store-secret reads any existing secret's etag and writes a conditional update (no silent lost-update; reports create vs overwrite), and on the no-value edge still surfaces the created key before failing. Tests updated for the selector path and added: schema CLI table/unknown-type, store-secret new-vs-existing(etag). 990 unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maximelb
added a commit
that referenced
this pull request
Jun 19, 2026
* auth login: allow user-scoped API key without --oid (#289) The validator currently rejects `auth login --uid X --api-key Y` with "Error: --oid and --api-key are required for API key login.", even though `--uid` is documented as the flag for user-scoped API keys. This makes brand-new accounts un-bootstrappable from the CLI because they have no organization yet — chicken-and-egg, since the user can't list/create their first org without working CLI auth, but can't auth without an OID, but doesn't have an OID until they create an org. The User API Key itself is a fully valid credential — env-var auth (LC_UID + LC_API_KEY) works for `auth list-orgs` already, which is the only call needed to bootstrap from "I have a User API Key" to "I know my OIDs". The bug was purely in the `auth login` validator forcing an --oid constraint that the rest of the CLI doesn't need. Fix: rework the validator to accept either: --oid + --api-key (org-scoped key — existing behavior) --uid + --api-key (user-scoped key — new path, --oid optional) When neither --oid nor --uid is provided, emit a clear error distinguishing the two key types. The `write_credentials` call already handles oid=None correctly (skips writing the field), so no downstream changes needed. Tested: - `auth login --uid X --api-key Y` (no --oid) → succeeds - `auth list-orgs` against those credentials → returns the user's orgs - `auth login --api-key Y` (neither --oid nor --uid) → clear error - `auth login --oid X --api-key Y` (existing org-scoped path) → unchanged - `auth login --oauth --provider google` → unchanged * auth login: clear stale oid on user-scoped re-login + tests (#291) Follow-up to #289. When a user re-runs `auth login --uid X --api-key Y` in an environment that previously held an `--oid + --api-key` login, the old `oid` survived in config because `write_credentials` only writes fields it is given. Subsequent commands would silently pair the new user-scoped credentials with the stale org context. Fix it by dropping the `oid` field from the affected env block right after the write whenever the login was user-scoped (`uid` set, `oid` unset). Mirrors the post-write cleanup pattern already used by the OAuth flow for `api_key`. Also refresh the `--ai-help` text so it advertises the user-scoped shape (`--uid + --api-key`, no `--oid`) that #289 enabled, and add unit tests for the validator branches and the stale-oid cleanup -- neither was covered before. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * vulnerability: fix search payload key + complete extension coverage (#293) The CLI's search dict was sending {"search": <field>, ...} but the extension's parseSearchOp reads s["field"] — so every --search-* call was silently dropped server-side. Switch to {"field": ...} and update the assertion that locked in the wrong shape. Round out the rest of the surface the extension exposes: - new flags: --include-enrichment, --filter-via-state on the list commands; --normalized-package-name on cve hosts; --rollup-subpackages on host packages; --include-enrichment on cve get / cve packages - new subcommands: - vuln cve epss-history (query_epss_history) - vuln finding resolve / bulk-resolve / list / reset (set_finding_resolution, bulk_set_finding_resolution, list_finding_resolutions, reset_asset_findings) - vuln snapshot list (query_daily_snapshots) - help text now mentions lc_risk as a valid --sort-by on cve list and host packages Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ai: forward X-LC-UID on org-scoped AI session calls (#294) User API Keys are scoped to a user, not an org, so jwt.limacharlie.io can only resolve them when the UID accompanies the secret. The AI SDK sent X-LC-OID + Authorization but never X-LC-UID, so a User API Key could not authenticate ai start-session (or any ai session command) and the server rejected it as an invalid org key. ai-sessions' OrgDualAuthMiddleware already reads X-LC-UID for the raw-API-key path (and ignores it for JWT auth), so no server change is needed -- the SDK just has to send it. Add AI._org_auth_headers() which always sets X-LC-OID and adds X-LC-UID when the client has a uid, and route both start_session() and _org_request() through it so the entire org-scoped AI surface (start-session, session list/get/terminate/history, usage) works under a User API Key. Tests: fix the mock_org fixture to set _uid = None (otherwise the MagicMock auto-vivifies a truthy _uid and leaks X-LC-UID into every request) and add TestOrgRequestUidHeader covering both the org-scoped (no header) and user-scoped (header forwarded) cases for start_session and the _org_request path. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * schema: add 'schema reset' command to rebuild org schemas (#295) Exposes the existing backend endpoint DELETE /orgs/{oid}/schema (resetOrgSchemas) through the Python CLI/SDK, mirroring the Go SDK's Organization.ResetSchemas(). - SDK: Organization.reset_schemas() -> DELETE orgs/{oid}/schema - CLI: 'limacharlie schema reset', guarded by --confirm (exit 4 without it), with register_explain text for --ai-help - Tests: SDK request assertion, CLI tests for the --confirm guard and the happy path, plus a 'schema list' regression guard; updated the lazy-loading regression's expected subcommand set Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * cli: add --enabled/--disabled flag to hive set commands (#296) Hive records are created disabled by default, which surprises operators (and LLMs) who run e.g. `secret set` and expect the record to be live. Add an `--enabled/--disabled` flag to the create/update commands so a record can be created and enabled in one shot: limacharlie secret set --key foo --input-file foo.yaml --enabled limacharlie lookup set --key bar --input-file bar.yaml --enabled limacharlie hive set --hive-name lookup --key baz --input-file f --enabled limacharlie dr set --key my-rule --input-file rule.yaml --enabled When passed, the flag overrides any usr_mtd.enabled value in the input file. When omitted, behavior is unchanged: the input file's value (if any) is preserved, otherwise the server-side default applies. The change is scoped to the three entry points that create hive records: - `_hive_shortcut.py` — covers secret, lookup, playbook, ai-skill, cloud-adapter, external-adapter, fp, note, sop - `hive.py` — generic `hive set` - `dr.py` — `dr set` Help text on each updated to call out the disabled-by-default behavior and the new flag. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * cli: clarify event overview/timeline/list --ai-help to prevent unreachable polling predicates (#297) A real AI session burned its full 5-minute foreground-Bash timeout polling "event overview" with `grep -q event_type` — a predicate unreachable by construction because overview returns only millisecond-epoch timestamp buckets, no event content. The previous --ai-help said overview gave a "high-level summary of event activity" without showing the output shape, which an AI can reasonably read as "richer than just timestamps". This commit extends the three relevant explain blocks so any AI reading --ai-help cold can pick the right tool the first time: - event overview: shows the actual output shape (flat list of ms epochs), what empty looks like (`[]`), what overview is good for, and an explicit "do not use this for sampling content or for predicates keyed on in-payload fields — those are unreachable, you will spin until timeout." - event timeline: alias, points at event overview's expanded text. - event list: notes the empty result is the literal `[]` and that structural empty-vs-non-empty against `[]` is the reliable presence predicate — not greping for in-payload strings. No behavior change; --ai-help text only. * cli: fix org quota help text + add dr enable/disable aliases (#298) * cli: fix misleading org quota --quota help text The --quota option claimed "0 to remove limit", implying 0 makes the quota unlimited. The backend (doQuotaChange) sets the value as the org's licensed sensor count (Stripe subscription quantity); 0 puts the org on the free tier (no paid quota), the opposite of "remove limit". Updated the option help and the AI explain text to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: add 'dr enable' and 'dr disable' aliases D&R rules are hive-backed, but the dr command group lacked the enable/disable convenience aliases that hive (and the per-hive shortcuts) provide. Add them mirroring the hive pattern: read the record metadata first (preserving tags/expiry/comment) and toggle only usr_mtd.enabled, routing to the /mtd endpoint. Honors --namespace (general/managed/service). Includes --ai-help explain text and unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * CLI: discoverability & ergonomics improvements (projection flags, scaffolding, clearer verdicts) (#299) * cli: discoverability & ergonomics improvements Proactive developer-experience improvements found during routine review of the CLI surface. Each change is small and self-contained. - Global projection flags: add --fields, --sort-by, --reverse on the root group, mirroring the existing --filter module-level mechanism in output.py so they flow into every command's render path. - api-key list --name: filter the key-hash-keyed result down to the single matching key while preserving the raw object shape. - start-session: accept the hive://ai_agent/<name> URI form (the form the D&R 'start ai agent' action uses) in addition to a bare key. - hive validate: emit an explicit positive verdict ("Record is valid." to stderr; {"valid": true} for json/yaml when the API is silent), keeping stdout machine-stable. - hive set: add --tag-add/--tag-rm (additive), --comment, --expiry; metadata-only update when no data is supplied, overrides otherwise. - secret set --value and a 'tag' subcommand (add/rm/set) on hive shortcut groups; --value documents the shell-history exposure. - dr set --detect/--respond/--tag: assemble a rule from component files (mutually exclusive with --input-file). - cloud-adapter/external-adapter list-types: derive supported adapter types from the cloud_sensor schema with a curated fallback; fix stale "...and others" prose that omitted threatlocker. - hive schema: default to a flat field table (resolving $ref/$defs); raw JSON-Schema still available via --output json. - event types: note that an empty result on a fresh org is expected (the schema is observed, not declared). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: generalize hive shortcut --value via per-hive value_key The shared hive shortcut set command exposed --value wrapping the input as {data: {secret: <value>}} for every hive, but only the secret hive uses a single "secret" data field — the wrapper was meaningless (and wrong) for the structured-data hives (lookup, fp, playbook, note, sop, adapters, ai-skill). make_hive_group now takes an optional value_key naming the hive's single scalar data field. --value is offered only when value_key is set (the secret group declares value_key="secret") and wraps as {data: {<value_key>: <value>}}. Structured-data hives no longer advertise --value at all. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: fix adapter list-types to enumerate real adapter types per hive list-types derived its list from the bare JSON-Schema $defs keys, which are helper structs (ClientOptions, AckBufferOptions, Dict, …), not adapter types — so it printed garbage. The reflected schema is a root that $refs into the record definition (CloudSensorRecord / ExternalAdapterConfig); the real type names are that record's properties (s3, office365, threatlocker, …) minus the sensor_type discriminator. - Resolve the root $ref into the record and use its properties (fall back to inline root properties); never enumerate raw $defs keys. - Parameterize by hive so cloud-adapter reads cloud_sensor and external-adapter reads external_adapter (their type sets genuinely differ). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ai: resolve ai-sessions URL from per-org service URLs instead of hardcoding (#300) The AI session CLI/SDK commands (start session, chat, auth claude, list/ get/terminate, attach, usage) hardcoded https://ai.limacharlie.io as the ai-sessions host. The orgs/{oid}/url endpoint already returns a per-org `ai` entry (lc.SiteURLs.Ai) whose value depends on the deployment -- e.g. staging orgs return ai-staging.limacharlie.io -- so the hardcode pointed staging orgs at the production service. Resolve the host lazily from Organization.get_urls()["ai"] (cached, same pattern as Search), prefixing https:// when needed and falling back to the well-known production host only when the entry is absent. SessionAttachment resolves the same way when no explicit base_url override is given. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: adapter schema --type / sensors --key + api-key create --store-secret (#301) * cli: add adapter schema --type / sensors --key and api-key create --store-secret Three FDE-acceleration additions found while profiling AI agent build runs: - cloud-adapter/external-adapter `schema --type <t>`: resolve one adapter type's config sub-schema from the hive JSON-Schema $defs and render the flat field table (reusing hive's _flatten_schema). Shows where each field lives (e.g. hostname under client_options), preventing "unknown field" rejections from hand-built records. Unknown type errors with the valid type list. - cloud-adapter/external-adapter `sensors --key <adapter>`: return the live sensor(s) an adapter produced by matching the record's installation_key (iid) against the sensor iid — the reliable way to get an adapter's SID without decoding the installation key. Falls back to hostname match; reports a clear "not registered yet" when the adapter hasn't delivered events. - `api-key create --store-secret <name>` (+ --store-secret-tag): atomically mint the key and write its value into the secret hive, collapsing the mint -> capture -> store -> reference chain into one call (the value never has to transit an intermediate file). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: address review — quiet/json on adapter schema, server-side sensor selector, etag-safe store-secret - adapter `schema --type` now respects --quiet (consistent with sensors/list-types) and its --output json carries the root $defs so nested $refs stay resolvable. - adapter `sensors --key` filters server-side via a sensor selector (iid == "…" / hostname == "…") instead of paging and scanning every sensor. - api-key create --store-secret reads any existing secret's etag and writes a conditional update (no silent lost-update; reports create vs overwrite), and on the no-value edge still surfaces the created key before failing. Tests updated for the selector path and added: schema CLI table/unknown-type, store-secret new-vs-existing(etag). 990 unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: add org quota-usage command for getOrgQuotaUsage API (#302) Adds support for the GET /quota_usage/{oid} endpoint (getOrgQuotaUsage), exposing the enforced sensor quota usage the platform uses to gate sensors coming online. Unlike the online sensor count, this weights EPP/response-mode sensors, so it can read higher and is the correct value to size the sensor quota against. - SDK: Organization.get_quota_usage() returns {usage, quota, breakdown} - CLI: `limacharlie org quota-usage` with --ai-help explain text - Docs + unit tests (SDK, CLI, command-map lint, subcommand regression) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * cli: add app hive shortcut command (#303) Add an `app` hive-shortcut group mirroring the other hive shortcuts (secret, lookup, playbook, note, sop, ...) for the new `app` hive added in legion_config_hive. The app hive holds user-authored, AI-generated mini web apps (a single self-contained HTML document rendered in a sandboxed iframe by the web UI). - New limacharlie/commands/app.py via make_hive_group("app", "app", "app") with app-specific --ai-help/explain text covering the record shape (display_name, html, required_permissions, allowed_origins, required_services, locations, expected_context). - Register "app" in cli.py _COMMAND_MODULE_MAP. - Update lazy-loading regression test (top-level set, module map, subcommands map, hive-shortcut load test). - Document the shortcut in doc/cli/hive-data.md. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: remove stale cli-v2 from CI trigger branches master is now the source of truth for the CLI v5 line; the cli-v2 branch trigger is leftover and runs only redundant unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011t7VKJidmVWW8d8EP3v6sD --------- Co-authored-by: Chris Botelho <chris.botelho@limacharlie.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three CLI additions that remove friction in automated/agent build flows (split out of the merged #299; this is the remaining commit).
cloud-adapter/external-adapter schema --type <t>— resolve one adapter type's config sub-schema from the hive JSON-Schema$defsand render the flat field table (reusing hive's_flatten_schema). Shows where each field lives (e.g.hostnameunderclient_options), preventingunknown fieldrejections from hand-built records. Unknown--typeerrors with the valid type list.cloud-adapter/external-adapter sensors --key <adapter>— return the live sensor(s) an adapter produced by matching the record'sinstallation_key(iid) against the sensoriid— the reliable way to get an adapter's SID without decoding the installation key. Falls back to hostname match; reports a clear "not registered yet" when the adapter hasn't delivered events.api-key create --store-secret <name>(+--store-secret-tag) — atomically mint the key and write its value into the secret hive, collapsing the mint → capture → store → reference chain into one call (the value never transits an intermediate file).Unit-tested (986 passing; new coverage in
test_cli_ergonomics.py, adapter-group subcommand list updated in the lazy-loading regression test) and smoke-tested live.🤖 Generated with Claude Code